import { useMemoizedFn } from 'ahooks'
import type { LogMessage } from 'electron-log'
import { ChevronDown, ChevronUp, Terminal } from 'lucide-react'
import { useCallback, useEffect, useId, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { IPC_CHANNELS } from 'shared/ipcChannels'
import { Button } from '@/components/ui/button'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Switch } from '@/components/ui/switch'
import { useIpcListener } from '@/hooks/useIpc'
import { cn } from '@/lib/utils'

interface ParsedLog {
  id: string
  timestamp: string
  module: string
  level: string
  message: string
}

// interface LogMessage {
//   logId: string
//   date: Date
//   scope?: string
//   level: string
//   data: string[]
// }

const MAX_LOG_MESSAGES = 200 // 仅展示最近的 200 条日志

interface LogDisplayerProps {
  expanded?: boolean
  onExpandedChange?: (expanded: boolean) => void
}

export default function LogDisplayer({ expanded = true, onExpandedChange }: LogDisplayerProps) {
  const [logMessages, setLogMessages] = useState<ParsedLog[]>([])
  const [autoScroll, setAutoScroll] = useState(true)
  const scrollAreaRef = useRef<HTMLDivElement>(null)
  const viewportRef = useRef<HTMLDivElement>(null)
  
  // 悬浮球拖动相关状态
  const [isDragging, setIsDragging] = useState(false)
  const [position, setPosition] = useState(() => {
    // 从 localStorage 读取保存的位置，默认为右下角
    try {
      const saved = localStorage.getItem('logDisplayer:floatingBallPosition')
      if (saved) {
        return JSON.parse(saved)
      }
    } catch (error) {
      console.warn('读取悬浮球位置失败:', error)
    }
    return { x: window.innerWidth - 80, y: window.innerHeight - 80 } // 默认右下角，使用 top 坐标系
  })
  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 })
  const [isMouseDown, setIsMouseDown] = useState(false)
  const [dragStartPos, setDragStartPos] = useState({ x: 0, y: 0 })
  const [hasMoved, setHasMoved] = useState(false)
  const floatingBallRef = useRef<HTMLDivElement>(null)

  const handleToggleExpanded = useCallback(() => {
    onExpandedChange?.(!expanded)
  }, [expanded, onExpandedChange])

  const scrollToBottom = useCallback(() => {
    if (viewportRef.current && autoScroll) {
      const scrollContainer = viewportRef.current
      requestAnimationFrame(() => {
        scrollContainer.scrollTop = scrollContainer.scrollHeight
      })
    }
  }, [autoScroll])

  const parseLogMessage = (log: LogMessage): ParsedLog | null => {
    if (!log.data || log.data.length === 0) {
      return null
    }
    return {
      id: crypto.randomUUID(),
      timestamp: log.date.toLocaleString(),
      module: log.scope ?? 'App',
      level: typeof log.level === 'string' ? log.level.toUpperCase() : 'INFO',
      message: log.data.map(String).join(' '),
    }
  }

  useEffect(() => {
    // 监听 ScrollArea 的 viewport 元素
    if (scrollAreaRef.current) {
      const viewport = scrollAreaRef.current.querySelector<HTMLDivElement>(
        '[data-radix-scroll-area-viewport]',
      )
      if (viewport) {
        // 使用 MutableRefObject 来避免只读属性错误
        viewportRef.current = viewport
      }
    }
  }, [])

  // biome-ignore lint/correctness/useExhaustiveDependencies: parseLogMessage 不影响
  const handleLogMessage = useCallback(
    (message: LogMessage) => {
      const parsed = parseLogMessage(message)
      if (parsed) {
        setLogMessages(prev => [...prev.slice(-MAX_LOG_MESSAGES + 1), parsed])
        if (autoScroll && expanded) {
          scrollToBottom()
        }
      }
    },
    [autoScroll, scrollToBottom, expanded],
  )

  useIpcListener(IPC_CHANNELS.log, handleLogMessage)

  const autoScrollId = useId()

  // 悬浮球拖动事件处理
  const handleFloatingBallMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
    e.preventDefault()
    e.stopPropagation() // 阻止事件冒泡
    
    const ball = floatingBallRef.current
    if (!ball) return

    // 获取悬浮球当前位置
    const rect = ball.getBoundingClientRect()
    
    // 计算鼠标点击位置相对于悬浮球左上角的偏移量
    const offset = {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top
    }
    
    // 记录初始状态，但不立即开始拖动
    setDragOffset(offset)
    setIsMouseDown(true)
    setDragStartPos({ x: e.clientX, y: e.clientY })
    setHasMoved(false)
  }, [])

  const handleMouseMove = useCallback((e: MouseEvent) => {
    if (!isMouseDown) return

    // 计算移动距离
    const moveDistance = Math.sqrt(
      Math.pow(e.clientX - dragStartPos.x, 2) + Math.pow(e.clientY - dragStartPos.y, 2)
    )

    // 如果移动距离超过阈值，才开始真正的拖动
    const dragThreshold = 5 // 5px 的拖动阈值
    if (moveDistance > dragThreshold && !isDragging) {
      setIsDragging(true)
      setHasMoved(true)
    }

    // 如果正在拖动，更新位置
    if (isDragging || moveDistance > dragThreshold) {
      // 计算悬浮球左上角应该在的位置
      const newX = e.clientX - dragOffset.x
      const newY = e.clientY - dragOffset.y

      // 边界限制 - 确保悬浮球不会超出屏幕边界
      const ballSize = 56 // 14 * 4 = 56px (w-14 h-14)
      const maxX = window.innerWidth - ballSize
      const maxY = window.innerHeight - ballSize

      const boundedPosition = {
        x: Math.max(0, Math.min(newX, maxX)),
        y: Math.max(0, Math.min(newY, maxY))
      }

      setPosition(boundedPosition)
      setHasMoved(true)
    }
  }, [isMouseDown, isDragging, dragOffset, dragStartPos])

  // 处理鼠标释放事件
  const handleMouseUp = useCallback(() => {
    // 如果没有移动，则触发点击事件
    if (!hasMoved && isMouseDown) {
      handleToggleExpanded()
    }

    // 如果发生了拖动，进行吸附处理
    if (isDragging) {
      const ballSize = 56
      const snapThreshold = 20 // 吸附阈值

      setPosition((prevPosition: { x: number; y: number }) => {
        let finalPosition = { ...prevPosition }

        // 左右边缘吸附
        if (finalPosition.x < snapThreshold) {
          finalPosition.x = 0
        } else if (finalPosition.x > window.innerWidth - ballSize - snapThreshold) {
          finalPosition.x = window.innerWidth - ballSize
        }

        // 上下边缘吸附 - 修正为 top 坐标系
        if (finalPosition.y < snapThreshold) {
          finalPosition.y = 0
        } else if (finalPosition.y > window.innerHeight - ballSize - snapThreshold) {
          finalPosition.y = window.innerHeight - ballSize
        }

        // 保存到 localStorage
        try {
          localStorage.setItem('logDisplayer:floatingBallPosition', JSON.stringify(finalPosition))
        } catch (error) {
          console.warn('保存悬浮球位置失败:', error)
        }

        return finalPosition
      })
    }

    // 重置所有状态
    setIsDragging(false)
    setIsMouseDown(false)
    setHasMoved(false)
  }, [hasMoved, isMouseDown, isDragging, handleToggleExpanded])

  // 监听全局鼠标事件
  useEffect(() => {
    if (isMouseDown) {
      document.addEventListener('mousemove', handleMouseMove)
      document.addEventListener('mouseup', handleMouseUp)
      
      if (isDragging) {
        document.body.style.userSelect = 'none' // 禁用文本选择
        document.body.style.cursor = 'grabbing'
      }
      
      return () => {
        document.removeEventListener('mousemove', handleMouseMove)
        document.removeEventListener('mouseup', handleMouseUp)
        document.body.style.userSelect = ''
        document.body.style.cursor = ''
      }
    }
  }, [isMouseDown, isDragging, handleMouseMove, handleMouseUp])

  // 监听窗口大小变化，确保悬浮球不会超出新的边界
  useEffect(() => {
    const handleResize = () => {
      const ballSize = 56
      const maxX = window.innerWidth - ballSize
      const maxY = window.innerHeight - ballSize

      setPosition((prevPosition: { x: number; y: number }) => ({
        x: Math.max(0, Math.min(prevPosition.x, maxX)),
        y: Math.max(0, Math.min(prevPosition.y, maxY))
      }))
    }

    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])

  return (
    <>
      {expanded ? (
        <div className="h-[180px] bg-white border-t shadow-inner">
          <div className="h-full flex flex-col bg-background">
            {/* 日志头部 */}
            <div className="flex items-center justify-between px-4 py-2 border-b">
              <div className="flex items-center gap-2">
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={handleToggleExpanded}
                  className="h-auto p-1 hover:bg-muted"
                >
                  <ChevronUp className="h-4 w-4" />
                </Button>
                <h3 className="font-medium">运行日志</h3>
                <span className="text-xs text-muted-foreground">
                  {logMessages.length} 条记录
                </span>
              </div>
              <div className="flex items-center gap-4">
                {/* 自动滚动开关 */}
                <div className="flex items-center gap-2">
                  <Switch
                    id={autoScrollId}
                    checked={autoScroll}
                    onCheckedChange={setAutoScroll}
                  />
                  <label
                    htmlFor={autoScrollId}
                    className="text-xs text-muted-foreground cursor-pointer select-none"
                  >
                    自动滚动
                  </label>
                </div>
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={() => {
                    setLogMessages([])
                    scrollToBottom()
                  }}
                  className="text-xs h-7 px-2 text-muted-foreground hover:text-destructive"
                >
                  清空
                </Button>
              </div>
            </div>

            {/* 日志内容 */}
            <ScrollArea ref={scrollAreaRef} className="flex-1">
              <div className="p-4 font-mono text-sm">
                {logMessages.map((log, index) => (
                  <LogItem key={log.id} log={log} index={index} />
                ))}
              </div>
            </ScrollArea>
          </div>
        </div>
      ) : null}
      
      {/* 悬浮球 - 通过Portal渲染到body，避免父容器限制 */}
      {!expanded && createPortal(
        <div 
          ref={floatingBallRef}
          className={cn(
            "fixed z-50 transition-all duration-200 ease-out",
            isDragging ? "scale-110" : "scale-100"
          )}
          style={{
            left: `${position.x}px`,
            top: `${position.y}px`,
            cursor: isDragging ? 'grabbing' : 'grab',
            transform: `scale(${isDragging ? 1.1 : 1})`,
            transition: isDragging ? 'none' : 'all 0.2s ease-out'
          }}
          onMouseDown={handleFloatingBallMouseDown}
        >
          <div className="relative">
            <Button
              className={cn(
                "w-14 h-14 rounded-full shadow-lg hover:shadow-xl transition-all duration-200 bg-primary hover:bg-primary/90 text-primary-foreground select-none",
                !isDragging && "hover:scale-110",
                isDragging && "scale-110 shadow-2xl"
              )}
              title={`运行日志 (${logMessages.length} 条记录) - 可拖动`}
              style={{ pointerEvents: 'none' }} // 禁用Button自身的点击，由父元素处理
            >
              <Terminal className="h-5 w-5" />
            </Button>
            {logMessages.length > 0 && (
              <div 
                className={cn(
                  "absolute -top-2 -right-2 bg-destructive text-destructive-foreground rounded-full min-w-[20px] h-5 flex items-center justify-center text-xs font-medium shadow-sm transition-transform duration-150",
                  isDragging && "scale-110"
                )}
                style={{ pointerEvents: 'none' }}
              >
                {logMessages.length > 99 ? '99+' : logMessages.length}
              </div>
            )}
          </div>
        </div>,
        document.body
      )}
    </>
  )
}

function LogItem({ log, index }: { log: ParsedLog; index: number }) {
  const getLevelColor = useMemoizedFn((level: string): string => {
    switch (level.toUpperCase()) {
      case 'ERROR':
        return 'text-destructive font-medium'
      case 'FATAL':
        return 'text-destructive font-bold'
      case 'WARN':
        return 'text-warning font-medium'
      case 'DEBUG':
        return 'text-blue-600'
      case 'INFO':
        return 'text-muted-foreground'
      case 'SUCCESS':
        return 'text-emerald-600'
      case 'NOTE':
        return 'text-purple-600'
      default:
        return 'text-muted-foreground'
    }
  })
  return (
    <div
      key={log.id}
      className={cn(
        'flex gap-2 items-start py-1 whitespace-nowrap',
        index % 2 === 0 ? 'bg-muted/40' : 'bg-background',
      )}
    >
      <span className="text-muted-foreground shrink-0">[{log.timestamp}]</span>
      <span className="text-foreground/70 shrink-0">[{log.module}]</span>
      <span className={cn('shrink-0', getLevelColor(log.level))}>
        {log.level}
      </span>
      <span className="text-foreground truncate">{log.message}</span>
    </div>
  )
}
